package com.softbank.speaker;

import com.softbank.speaker.utils.ConnectionOptionWrapper;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

public class SpeakerMQTTClient {
    /**
     * MQ4IOT支持子级 topic，用来做自定义的过滤，此处为示意，可以填写任何字符串，具体参考https://help.aliyun.com/document_detail/42420.html?spm=a2c4g.11186623.6.544.1ea529cfAO5zV3
     * 需要注意的是，完整的 topic 长度不得超过128个字符。
     */
    private String mq4IotTopicTemplate;
    /**
     * QoS参数代表传输质量，可选0，1，2，根据实际需求合理设置，具体参考 https://help.aliyun.com/document_detail/42420.html?spm=a2c4g.11186623.6.544.1ea529cfAO5zV3
     */
    int qosLevel = 0;
    private MqttClient mqttClient;

    public SpeakerMQTTClient(String instanceId, String endPoint, String accessKey, String secretKey, String parentTopic, String GID, String clientId, int qosLevel) throws MqttException, InvalidKeyException, NoSuchAlgorithmException {
        this.mq4IotTopicTemplate = parentTopic + "/order/" + GID + "@@@%s";
        this.qosLevel = qosLevel;
        ConnectionOptionWrapper connectionOptionWrapper = new ConnectionOptionWrapper(instanceId, accessKey, secretKey, clientId);
        final MemoryPersistence memoryPersistence = new MemoryPersistence();
        /**
         * 客户端使用的协议和端口必须匹配，具体参考文档 https://help.aliyun.com/document_detail/44866.html?spm=a2c4g.11186623.6.552.25302386RcuYFB
         * 如果是 SSL 加密则设置ssl://endpoint:8883
         */
        mqttClient = new MqttClient("tcp://" + endPoint + ":1883", clientId, memoryPersistence);
        /**
         * 客户端设置好发送超时时间，防止无限阻塞
         */
        mqttClient.setTimeToWait(5000);
        mqttClient.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                /**
                 * 客户端连接成功后就需要尽快订阅需要的 topic
                 */
                System.out.println("connect success");
            }

            @Override
            public void connectionLost(Throwable throwable) {
                throwable.printStackTrace();
            }

            @Override
            public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
                /**
                 * 消费消息的回调接口，需要确保该接口不抛异常，该接口运行返回即代表消息消费成功。
                 * 消费消息需要保证在规定时间内完成，如果消费耗时超过服务端约定的超时时间，对于可靠传输的模式，服务端可能会重试推送，业务需要做好幂等去重处理。超时时间约定参考限制
                 * https://help.aliyun.com/document_detail/63620.html?spm=a2c4g.11186623.6.546.229f1f6ago55Fj
                 */
                System.out.println(
                        "receive msg from topic " + s + " , body is " + new String(mqttMessage.getPayload()));
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
                System.out.println("send msg succeed topic is : " + iMqttDeliveryToken.getTopics()[0]);
            }
        });
        mqttClient.connect(connectionOptionWrapper.getMqttConnectOptions());
    }

    private static SpeakerMQTTClient speakerMqttClient;

    public static synchronized SpeakerMQTTClient init(String instanceId, String endPoint, String accessKey, String secretKey, String parentTopic, String GID, String clientId, int qosLevel) throws MqttException, NoSuchAlgorithmException, InvalidKeyException {
        if (speakerMqttClient == null)
            speakerMqttClient = new SpeakerMQTTClient(instanceId, endPoint, accessKey, secretKey, parentTopic, GID, clientId, qosLevel);
        return speakerMqttClient;
    }

    public static SpeakerMQTTClient getInstance() {
        return speakerMqttClient;
    }

    /**
     * 播报
     *
     * @param amount     订单金额，单位：分
     * @param orderId    交易单号，默认最大长度32字节
     * @param transTime  交易时间，格式：yyyy-MM-dd hh:mm:ss  如：2019-05-10 14:48:31
     * @param cancelTime 取消支付时间，格式：yyyy-MM-dd hh:mm:ss  如：2019-05-10 14:48:31
     */
    public void speak(TransType transType,
                      PayType payType,
                      String amount,
                      String orderId,
                      String transTime,
                      String cancelTime,
                      String deviceSN) throws MqttException {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        Date time = calendar.getTime();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateTime = simpleDateFormat.format(time);
        String orderMsg;
        if (transType.code == TransType.ORDER.code) {
            orderMsg = "{\"type\":\""
                    + transType.value
                    + "\",\"amount\":\""
                    + amount
                    + "\",\"orderId\":\""
                    + orderId
                    + "\",\"transTime\":\""
                    + transTime
                    + "\",\"paymentChannel\":\""
                    + payType.value + "\"}";
        } else {
            orderMsg = "{\"type\":\""
                    + transType.value
                    + "\",\"amount\":\""
                    + amount
                    + "\",\"orderId\":\""
                    + orderId
                    + "\",\"cancelTime\":\""
                    + cancelTime
                    + "\",\"paymentChannel\":\""
                    + payType.value + "\"}";
        }
        System.out.println(orderMsg);
        MqttMessage message = new MqttMessage(orderMsg.getBytes());
        message.setQos(qosLevel);
        /**
         *  发送普通消息时，topic 必须和接收方订阅的 topic 一致，或者符合通配符匹配规则
         */
        mqttClient.publish(String.format(mq4IotTopicTemplate, deviceSN), message);
    }

    public static class SpeakerOrder {
        /**
         * 消息类型，可选字段，当不带该字段时默认为交易订单消息推送。
         * <ul>
         *     <li>transOrder：交易订单消息</li>
         *     <li>trans_cancel：用户取消支付消息</li>
         * </ul>
         */
        private String type;
        /**
         * 交易金额，十进制字符串，单位：分
         */
        private String amount;
        /**
         * 交易单号，默认最大长度小于32字节
         */
        private String orderId;
        /**
         * 交易类型，包括：
         * WXZF：微信收款
         * ZFBZF：支付宝到账
         * UPSMPAY：银联收款
         * hapay: 乐付
         */
        private String paymentChannel;
        /**
         * 交易时间，格式：yyyy-MM-dd hh:mm:ss  如：2019-05-10 14:48:31
         */
        private String transTime;
        /**
         * 取消支付时间，格式：yyyy-MM-dd hh:mm:ss  如：2019-05-10 14:48:31
         */
        private String cancelTime;
        /**
         * 是否需要原路返回消息执行状态，数值。可选字段。默认无回执
         * 1: 需要回执
         */
        private int re;

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getAmount() {
            return amount;
        }

        public void setAmount(String amount) {
            this.amount = amount;
        }

        public String getOrderId() {
            return orderId;
        }

        public void setOrderId(String orderId) {
            this.orderId = orderId;
        }

        public String getPaymentChannel() {
            return paymentChannel;
        }

        public void setPaymentChannel(String paymentChannel) {
            this.paymentChannel = paymentChannel;
        }

        public String getTransTime() {
            return transTime;
        }

        public void setTransTime(String transTime) {
            this.transTime = transTime;
        }

        public String getCancelTime() {
            return cancelTime;
        }

        public void setCancelTime(String cancelTime) {
            this.cancelTime = cancelTime;
        }

        public int getRe() {
            return re;
        }

        public void setRe(int re) {
            this.re = re;
        }
    }

    public enum TransType {
        ORDER(0, "transOrder"),
        CANCEL(1, "trans_cancel");
        private Integer code;
        private String value;

        TransType(int code, String value) {
            this.code = code;
            this.value = value;
        }

        public static Integer getCode(Integer code) {
            TransType[] transTypes = values();
            for (TransType transType : transTypes) {
                if (transType.code().equals(code)) {
                    return transType.code();
                }
            }
            return null;
        }

        public static String getValue(Integer code) {
            TransType[] transTypes = values();
            for (TransType transType : transTypes) {
                if (transType.code().equals(code)) {
                    return transType.value();
                }
            }
            return null;
        }

        public Integer code() {
            return code;
        }

        public String value() {
            return value;
        }
    }

    public enum PayType {
        WXZF(0, "WXZF"),
        ZFBZF(1, "ZFBZF"),
        UPSMPAY(2, "UPSMPAY"),
        HAPAY(3, "hapay");
        private Integer code;
        private String value;

        PayType(int code, String value) {
            this.code = code;
            this.value = value;
        }

        public static Integer getCode(Integer code) {
            PayType[] payTypes = values();
            for (PayType payType : payTypes) {
                if (payType.code().equals(code)) {
                    return payType.code();
                }
            }
            return null;
        }

        public static String getValue(Integer code) {
            PayType[] payTypes = values();
            for (PayType payType : payTypes) {
                if (payType.code().equals(code)) {
                    return payType.value();
                }
            }
            return null;
        }

        public Integer code() {
            return code;
        }

        public String value() {
            return value;
        }

    }


}
